package com.zhiyou.app.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Response;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.RequestUtils;
import com.zhiyou.app.common.constant.UserHandler;
import com.zhiyou.app.common.exception.BizException;
import com.zhiyou.app.common.util.SecCheckUtil;
import com.zhiyou.app.pojo.bo.PayResult;
import com.zhiyou.app.pojo.bo.SecCheckResult;
import com.zhiyou.app.pojo.entity.*;
import com.zhiyou.app.pojo.enums.*;
import com.zhiyou.app.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WechatServiceImpl implements WechatService {
    @Resource
    private WxPayService wxPayService;
    @Resource
    private IOrderService orderService;
    @Resource
    private IWechatPayHistoryService wechatPayHistoryService;
    @Resource
    private IComWalletService comWalletService;
    @Resource
    private IUserService userService;
    @Resource
    private IUserWalletService userWalletService;
    @Resource
    private IWalletHistoryService walletHistoryService;
    @Resource
    private IWechatRefundHistoryService wechatRefundHistoryService;
    @Resource
    private IWithdrawalHistoryService withdrawalHistoryService;
    @Value("${wallet.com-id:1}")
    private String comWalletId;
    @Resource
    private IOrderGoodsService orderGoodsService;

    // 存储订单号与对应的CompletableFuture
    private final Map<String, CompletableFuture<PayResult>> waitingRequests = new ConcurrentHashMap<>();

    // 用于安全地操作共享数据
    private final ReentrantLock lock = new ReentrantLock();

    @Resource
    private WxMaService wxMaService;
    @Resource
    private WxMaConfig wxMaConfig;
    @Resource
    private WxMaProperties wxMaProperties;

    /**
     * 微信回调
     *
     * @param request 请求
     * @return 回复微信
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String notify(HttpServletRequest request) {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        Optional.ofNullable(timestamp).orElseThrow(() -> new RuntimeException("时间戳不能为空"));

        String nonce = request.getHeader("Wechatpay-Nonce");
        Optional.ofNullable(nonce).orElseThrow(() -> new RuntimeException("nonce不能为空"));

        String serialNo = request.getHeader("Wechatpay-Serial");
        Optional.ofNullable(serialNo).orElseThrow(() -> new RuntimeException("serialNo不能为空"));

        String signature = request.getHeader("Wechatpay-Signature");
        Optional.ofNullable(signature).orElseThrow(() -> new RuntimeException("signature不能为空"));

        log.info("请求头参数为：timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
        try {
            String notifyDataStr = RequestUtils.readData(request);
            JSONObject entries = JSONUtil.parseObj(notifyDataStr);
            SignatureHeader signatureHeader = new SignatureHeader(timestamp, nonce, signature, serialNo);
            //支付完成
            if (entries.getStr("event_type").equals("TRANSACTION.SUCCESS")) {
                WxPayNotifyV3Result wxPayNotifyV3Result = wxPayService.parseOrderNotifyV3Result(notifyDataStr, signatureHeader);
                String outTradeNo = wxPayNotifyV3Result.getResult().getOutTradeNo();
                if (outTradeNo != null) {
                    OrderEntity one = orderService.getOne(new LambdaQueryWrapper<OrderEntity>().eq(OrderEntity::getOrderNum, outTradeNo.substring(0, outTradeNo.indexOf("-"))));
                    if (one != null) {
                        if (one.getWxTransactionId() == null|| one.getWxTransactionId().contains(wxPayNotifyV3Result.getResult().getTransactionId())) {
                            handleWechatPay(one, wxPayNotifyV3Result);
                        } else {
                            //重复通知，直接返回成功
                            return WxPayNotifyV3Response.success("成功");
                        }
                    }
                }
                //退款
            } else if (entries.getStr("event_type").equals("REFUND.SUCCESS")) {
                WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = wxPayService.parseRefundNotifyV3Result(notifyDataStr, signatureHeader);
                WxPayRefundNotifyV3Result.DecryptNotifyResult result = wxPayRefundNotifyV3Result.getResult();
                RefundStatusEnum parse = RefundStatusEnum.parse(result.getRefundStatus());
                refundSuccess(result, parse);
                //商家转账，用户提现通知
            } else if (entries.getStr("event_type").equals("MCHTRANSFER.BILL.FINISHED")) {
                TransferBillsNotifyResult transferBillsNotifyResult = wxPayService.parseTransferBillsNotifyV3Result(notifyDataStr, signatureHeader);
                withdrawSuccess(transferBillsNotifyResult);
            } else {
                log.error("未知事件通知:{}", entries.getStr("event_type"));
                return WxPayNotifyV3Response.fail("未解析到处理支付类型");
            }
        } catch (Exception e) {
            log.error("微信回调数据解析异常:{}", e.getMessage(), e);
            return WxPayNotifyV3Response.fail("数据解析异常:" + e.getMessage());
        }
        return WxPayNotifyV3Response.success("成功");
    }

    /**
     * 等待支付结果
     *
     * @param payKey         支付KEY
     * @param timeoutSeconds 超时时间
     * @return 支付结果
     */
    @Override
    public CompletableFuture<PayResult> waitForPayResult(String payKey, int timeoutSeconds) {
        // 创建一个未完成的CompletableFuture
        CompletableFuture<PayResult> future = new CompletableFuture<>();
        // 存储订单对应的future
        waitingRequests.put(payKey, future);
        // 设置超时任务
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.schedule(() -> {
            // 超时后如果还未完成，则返回等待状态
            if (!future.isDone()) {
                future.complete(new PayResult(payKey, PayResultType.WAITING_RESPONSE, "WAITING"));
                // 从等待列表中移除
                waitingRequests.remove(payKey);
            }
            scheduler.shutdown();
        }, timeoutSeconds, TimeUnit.SECONDS);
        return future;
    }

    /**
     * 通知微信支付结果
     *
     * @param payKey  支付KEY
     * @param success 成功
     * @param message 消息
     */
    @Override
    public void notifyPayResult(String payKey, boolean success, String message) {
        lock.lock();
        try {
            // 从等待列表中获取对应的future
            CompletableFuture<PayResult> future = waitingRequests.get(payKey);
            if (future != null && !future.isDone()) {
                // 完成future，返回支付结果
                future.complete(new PayResult(
                        payKey,
                        success ? PayResultType.SUCCESS : PayResultType.FAIL,
                        message
                ));
                // 移除已处理的订单
                waitingRequests.remove(payKey);
            }
        } finally {
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void refundSuccess(WxPayRefundNotifyV3Result.DecryptNotifyResult result, RefundStatusEnum parse) {
        if (parse == RefundStatusEnum.SUCCESS) {
            comWalletService.addComWalletAmount(new BigDecimal(result.getAmount().getRefund()).multiply(new BigDecimal(-1)));
            orderService.update(new LambdaUpdateWrapper<OrderEntity>().eq(OrderEntity::getOrderNum, result.getOutTradeNo()).set(OrderEntity::getOrderStatus, OrderStatusEnum.REFUND));
            wechatRefundHistoryService.update(new LambdaUpdateWrapper<WechatRefundHistoryEntity>().eq(WechatRefundHistoryEntity::getOutRefundNum, result.getOutRefundNo())
                    .set(WechatRefundHistoryEntity::getStatus, parse).set(WechatRefundHistoryEntity::getTransactionId, result.getRefundId()));
            walletHistoryService.save(new WalletHistoryEntity().setAmount(new BigDecimal(result.getAmount().getRefund()).multiply(new BigDecimal(-1)).toString())
                    .setTransactionId(result.getRefundId()).setContent("退款订单[" + result.getOutTradeNo() + "]").setWalletType(WalletTypeEnum.COM).setWalletId(Integer.valueOf(comWalletId)));
        } else {
            orderService.update(new LambdaUpdateWrapper<OrderEntity>().eq(OrderEntity::getOrderNum, result.getOutTradeNo()).set(OrderEntity::getOrderStatus, OrderStatusEnum.FAIL_REFUND));
            wechatRefundHistoryService.update(new LambdaUpdateWrapper<WechatRefundHistoryEntity>().eq(WechatRefundHistoryEntity::getOutRefundNum, result.getOutRefundNo()).set(WechatRefundHistoryEntity::getStatus, parse));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void withdrawSuccess(TransferBillsNotifyResult transferBillsNotifyResult) {
        WithdrawalHistoryEntity withdrawalHistoryEntity = withdrawalHistoryService.getOne(new LambdaQueryWrapper<WithdrawalHistoryEntity>().eq(WithdrawalHistoryEntity::getOutWithdrawalNum, transferBillsNotifyResult.getResult().getOutBillNo()));
        withdrawalHistoryEntity.setStatus(WithdrawalStatusEnum.parse(transferBillsNotifyResult.getResult().getState()))
                .setTransactionId(transferBillsNotifyResult.getResult().getTransferBillNo());
        withdrawalHistoryService.updateById(withdrawalHistoryEntity);
        if (WithdrawalStatusEnum.parse(transferBillsNotifyResult.getResult().getState()) == WithdrawalStatusEnum.SUCCESS) {
            Integer transferAmount = transferBillsNotifyResult.getResult().getTransferAmount();
            userWalletService.update(new LambdaUpdateWrapper<UserWalletEntity>().eq(UserWalletEntity::getUserId, withdrawalHistoryEntity.getUserId())
                    .setSql("total_amount=total_amount-" + transferAmount).setSql("freeze_amount=freeze_amount-" + transferAmount).setSql("withdrawable_amount=withdrawable_amount-" + transferAmount));
            comWalletService.addComWalletAmount(new BigDecimal(transferAmount).multiply(new BigDecimal(-1)));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleWechatPay(OrderEntity one, WxPayNotifyV3Result wxPayNotifyV3Result) {
        one.setWxTransactionId(StrUtil.isEmptyIfStr(one.getWxTransactionId()) ? wxPayNotifyV3Result.getResult().getTransactionId() : one.getWxTransactionId() + "," + wxPayNotifyV3Result.getResult().getTransactionId());
        // 保存支付记录
        WechatPayHistoryEntity wechatPayHistoryEntity = new WechatPayHistoryEntity().setAmount(wxPayNotifyV3Result.getResult().getAmount().getTotal() + "").setContent(wxPayNotifyV3Result.getResult().getAttach()).setTransactionId(wxPayNotifyV3Result.getResult().getTransactionId()).setUserId(one.getUserId());
        //微信支付记录
        wechatPayHistoryService.save(wechatPayHistoryEntity);
        String[] split = wxPayNotifyV3Result.getResult().getOutTradeNo().split("-");
        String orderNum = split[0];
        List<Integer> gId = Arrays.stream(split).skip(1).map(Integer::parseInt).collect(Collectors.toList());
        //商品支付状态更新
        orderGoodsService.update(new LambdaUpdateWrapper<OrderGoodsEntity>().eq(OrderGoodsEntity::getOrderNum, orderNum).in(OrderGoodsEntity::getGoodsId, gId).set(OrderGoodsEntity::getPaid, 1));
        // 更新订单状态
        List<OrderGoodsEntity> list = orderGoodsService.list(new LambdaQueryWrapper<OrderGoodsEntity>().eq(OrderGoodsEntity::getOrderNum, one.getOrderNum()));
        one.setOrderStatus(list.stream().anyMatch(orderGoodsEntity -> orderGoodsEntity.getPaid() == 0) ? OrderStatusEnum.PART_PAID : OrderStatusEnum.PAID);
        one.setOrderAmount(one.getOrderAmount() + wxPayNotifyV3Result.getResult().getAmount().getTotal());
        one.setWxTransactionId(one.getWxTransactionId() == null ? wxPayNotifyV3Result.getResult().getTransactionId() : one.getWxTransactionId() + "," + wxPayNotifyV3Result.getResult().getTransactionId());
        orderService.updateById(one);
        //企业钱包数据更新
        comWalletService.addComWalletAmount(new BigDecimal(wxPayNotifyV3Result.getResult().getAmount().getTotal()));
        notifyPayResult(wxPayNotifyV3Result.getResult().getOutTradeNo(), true, "支付成功");
    }

    @Override
    public String notifyMa(HttpServletRequest request) {
        boolean checkSignature = wxMaService.checkSignature(request.getParameter("timestamp"), request.getParameter("nonce"), request.getParameter("signature"));
        if (!checkSignature) {
            throw new BizException("签名验证失败");
        }
        if (request.getMethod().toUpperCase(Locale.ROOT).equals("GET")) {
            // test
            return request.getParameter("echostr");
        } else if (request.getMethod().toUpperCase(Locale.ROOT).equals("POST")) {
            String notifyDataStr = RequestUtils.readData(request);
            WxMaMessage wxMaMessage = WxMaMessage.fromEncryptedJson(notifyDataStr, wxMaConfig);
            String appid = wxMaProperties.getAppid();
            // 微信小程序审核结果
            if ("event".equals(wxMaMessage.getMsgType()) && "wxa_media_check".equals(wxMaMessage.getEvent()) && appid.equals(wxMaMessage.getAppid())) {
                log.info(JSONUtil.toJsonPrettyStr(wxMaMessage));
                SecCheckUtil.notifySecCheckResult(wxMaMessage.getTraceId(), new SecCheckResult()
                        .setPassed(wxMaMessage.getResult().getSuggest().equals("pass"))
                        .setTransactionId(wxMaMessage.getTraceId())
                        .setErrMsg(SecCheckUtil.getLabel(wxMaMessage.getResult().getLabel())));
            }
        }
        return "";
    }
}
